<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>闭包</title>
</head>

<body>
    <script>
        /**
         * 闭包
         * 1. 如何产生闭包？

                当一个嵌套的内部（子）函数引用了嵌套的外部（父）函数的变量（函数）时，并且调用了外部函数，就产生了闭包

                闭包的个数 = 调用外部函数的次数

            2. 闭包到底是什么？

                闭包是嵌套的内部函数
                闭包是包含被引用变量（函数）的对象
        */
        /**
         * 案例1
         * 执行函数定义就会产生闭包（不用调用内部函数）
        */
        // function fn1() {
        //    //此时就已经产生闭包（因为函数提升）
        //     var a = 2;
        //     var b = 'abc'
        //     function fn2() {
        //         console.log(a)
        //     }
        // }
        // fn1()

        /**
         * 案例2
         * 将函数作为另外一个函数的返回值
         * 调用函数未赋值a
        */
        // function fn1() {
        //     var a = 2;
        //     var b = 'abc'
        //     function fn2() {
        //         console.log(a+1)// 未赋值a
        //     }
        //     return fn2
        // }
        // let ss = fn1() 
        // ss() // 3
        // ss() // 3
        // ss() // 3
        // 外部函数调用一次 ，1个闭包
        /**
         * 案例3
         * 将函数作为另外一个函数的返回值
         * 调用函数并赋值a
        */
        // function fn1() {
        //     var a = 2;
        //     var b = 'abc'
        //     function fn2() {
        //         console.log(a++)
        //     }
        //     return fn2
        // }
        // let ss = fn1()
        // ss() // 2
        // ss() // 3
        // ss() // 4

        /**
         * 案例4
         * 将函数作为实参传递给另一个函数调用
         * 调用函数并赋值a
        */
        // function showDelay(mes, time) {
        //     setTimeout(function () {
        //         alert(mes) // 如果将这句话注释掉，就没有产生闭包，说白了，必须在子函数里调用复函数的变量
        //     }, time)
        // }
        // showDelay('测试', 2000)
        /**
         * 3. 闭包的作用

                使函数内部的变量在函数执行完后，仍然存活在内存中（延长了局部变量的生命周期）
                让函数外部可以操作（读写）到函数内部的数据（变量/函数）
            4. 两个问题

                1.函数执行完后，函数内部声明的局部变量是否还存在？

                    一般是不存在，存在于闭包中的变量才可能存在，但也必须要有引用指向该函数。

                2.在函数外部能直接访问函数内部的局部变量吗？

                    不能，但我们可以通过闭包的形式让外部操作它
            5. 闭包的生命周期

                产生：在嵌套内部函数定义执行完时就产生了（不是在调用）
                死亡：在嵌套的内部函数成为垃圾对象时
        */
        /**
         * 6.闭包应用：自定义JS模块
 
             JS模块：具有特定功能的JS文件
 
             将所有的数据和功能都封装在一个函数内部
 
             只向外暴露一个包含n个方法的对象或函数
 
             模块的使用者，只需要通过模块暴露的对象调用方法来实现相应功能
 
 
        */
        // function myModule() {
        //     //私有数据
        //     var msg = 'My atguigu'
        //     //操作数据的函数
        //     function doSomething() {
        //         console.log('doSomething')
        //     }
        //     function doOtherthing() {
        //         console.log('doOtherthing')
        //     }
        //     //向外暴露
        //     return {
        //         doSomething: doSomething,
        //         doOtherthing: doOtherthing
        //     }
        // }
        // var module = myModule()
        // module.doSomething();
        // module.doOtherthing();
        // (function myModule() {
        //     //私有数据
        //     var msg = 'My atguigu'
        //     //操作数据的函数
        //     function doSomething() {
        //         console.log('doSomething')
        //     }
        //     function doOtherthing() {
        //         console.log('doOtherthing')
        //     }
        //     //向外暴露
        //     window.myModele2 = {
        //         doSomething: doSomething,
        //         doOtherthing: doOtherthing
        //     }
        // })()
        // module2.doSomething();
        // module2.doOtherthing();(function myModule() {
        //     //私有数据
        //     var msg = 'My atguigu'
        //     //操作数据的函数
        //     function doSomething() {
        //         console.log('doSomething')
        //     }
        //     function doOtherthing() {
        //         console.log('doOtherthing')
        //     }
        //     //向外暴露
        //     window.myModele2 = {
        //         doSomething: doSomething,
        //         doOtherthing: doOtherthing
        //     }
        // })()
        // module2.doSomething();
        // module2.doOtherthing();
        /**
        * 案例5
        * 作用域是在创建的时候建立的，跟调用无关this指代window
       */
        // var name = "The Window";
        // var object = {
        //     name: "My Object",
        //     getNameFunc: function () {
        //         return function () {
        //             return this.name;
        //         }
        //     }
        // }
        // alert(object.getNameFunc()()); // The Window 
        // 一定不能返错误，getNameFunc（）的调用者是object没错
        //调用完后，返回了一个函数，返回函数的this肯定是window啊，所以结果是 The Window 
        /**
           * 案例6
           * 作用域是在创建的时候建立的，跟调用无关this指代object2,that指代object2
          */
        // var name2 = "The Window"
        // var Object2 = {
        //     name2: "My Object",
        //     getNameFunc: function () {
        //         var that = this;
        //         return function () {
        //             return that.name2;
        //         }
        //     }
        // };
        // alert(Object2.getNameFunc()()); // My Object
        // 调用完后，返回了一个函数，返回函数的this肯定是window,但调用的是that.name2
        // var that = this，表示在调用getNameFunc（）函数的时候，将that保存为getNameFunc（）的this了
        //由Object调用的getNameFunc()，因此函数内this为Object，则that也为Object，输出为My Object
        /**
               * 案例7
               * 1:  箭头函数中的this 是如何查找的？
                 2:  箭头函数中的this 是向外层作用域中,  一层一层查找this,  直到找到this的定义。
                
                     箭头函数中的this   引用的就是最近作用域中的this.

              */
        var name2 = "The Window"
        var Object2 = {
            name2: "My Object",
            getNameFunc: function () {
                // var that = this;
                return () => {
                    return this.name2;
                }
            }
        };
        alert(Object2.getNameFunc()()); // My Object
    </script>
</body>

</html>